#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
@Time        : 2021/12/2 15:43
@Author      : Albert Darren
@Contact     : 2563491540@qq.com
@File        : mnist.py
@Version     : Version 1.0.0
@Description : TODO
@Created By  : PyCharm
"""
import numpy as np
# 使用listdir模块，用于访问本地文件
from os import listdir
from sklearn.neural_network import MLPClassifier


# 定义img2vector函数，将加载的32*32 的图片矩阵展开成一列向量
def img2vector(fileName):
    retMat = np.zeros([1024], int)
    fr = open(fileName)  # 打开包含32*32大小的数字文件
    lines = fr.readlines()  # 读取文件的所有行
    for i in range(32):
        for j in range(32):  # 将01数字存放在retMat
            retMat[i * 32 + j] = lines[i][j]
    return retMat


# 并将样本标签转化为one-hot向量
def readDataSet(path):
    fileList = listdir(path)  # 获取文件夹下所有文件
    numFiles = len(fileList)  # 统计需要读取的文件的数目
    dataSet = np.zeros([numFiles, 1024], int)  # 用于存放所有的数字文件
    hwLabels = np.zeros([numFiles, 10])  # 用于存放对应的标签one-hot
    for i in range(numFiles):
        filePath = fileList[i]  # 获取文件名称/路径
        digit = int(filePath.split('_')[0])
        hwLabels[i][digit] = 1.0
        dataSet[i] = img2vector(path + '/' + filePath)  # 读取文件内容
    return dataSet, hwLabels


train_dataSet, train_hwLabels = readDataSet('trainingDigits')

# 构建神经网络：设置网络的隐藏层数、各隐藏层神经元个数、
# 激活函数、学习率、优化方法、最大迭代次数。
# hidden_layer_sizes 存放的是一个元组，表示第i层隐藏层里神经元的个数
# 使用logistic激活函数和adam优化方法，并令初始学习率为0.0001
clf = MLPClassifier(hidden_layer_sizes=(50,), activation='logistic',
                    solver='adam', learning_rate_init=0.0001, max_iter=2000)
# fit函数能够根据训练集及对应标签集自动设置多层感知机的输入与输出层的神经元个数。
# 例如train_dataSet为n*1024的矩阵，train_hwLabels为n*10的矩阵，
# 则fit函数将MLP的输入层神经元个数设为1024，输出层神经元个数为 10.
clf.fit(train_dataSet, train_hwLabels)

# 测试集评价
dataSet, hwlLabels = readDataSet('testDigits')
res = clf.predict(dataSet)  # 对测试集进行预测
error_num = 0  # 统计预测错误的数目
num = len(dataSet)  # 测试集的数目
for i in range(num):
    # 比较长度为10的数组，返回包含01的数组，0为不同，1为相同
    if np.sum(res[i] == hwlLabels[i]) < 10:
        error_num += 1

error_rate = error_num / float(num)
accuracy = 1 - error_rate
print("测试手写体数字样本总数:{},精确度:{:.2%}，错误率:{:.2%}".format(num, accuracy, error_rate))
